/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// RTCP
// Implementation

#include "include/RTCP.hh"
#include "../groupsock/include/GroupsockHelper.hh"
#include "rtcp_from_spec.h"

#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
#define snprintf _snprintf
#endif
#define HACK_FOR_CHROME_WEBRTC_BUG 1 //#####@@@@@

////////// RTCPMemberDatabase //////////

class RTCPMemberDatabase {
public:
    RTCPMemberDatabase(RTCPInstance &ourRTCPInstance)
            : fOurRTCPInstance(ourRTCPInstance), fNumMembers(1 /*ourself*/),
              fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
    }

    virtual ~RTCPMemberDatabase() {
        delete fTable;
    }

    Boolean isMember(u_int32_t ssrc) const {
        return fTable->Lookup((char *) (long) ssrc) != NULL;
    }

    Boolean noteMembership(u_int32_t ssrc, unsigned curTimeCount) {
        Boolean isNew = !isMember(ssrc);

        if (isNew) {
            ++fNumMembers;
        }

        // Record the current time, so we can age stale members
        fTable->Add((char *) (long) ssrc, (void *) (long) curTimeCount);

        return isNew;
    }

    Boolean remove(u_int32_t ssrc) {
        Boolean wasPresent = fTable->Remove((char *) (long) ssrc);
        if (wasPresent) {
            --fNumMembers;
        }
        return wasPresent;
    }

    unsigned numMembers() const {
        return fNumMembers;
    }

    void reapOldMembers(unsigned threshold);

private:
    RTCPInstance &fOurRTCPInstance;
    unsigned fNumMembers;
    HashTable *fTable;
};

void RTCPMemberDatabase::reapOldMembers(unsigned threshold) {
    Boolean foundOldMember;
    u_int32_t oldSSRC = 0;

    do {
        foundOldMember = False;

        HashTable::Iterator *iter
                = HashTable::Iterator::create(*fTable);
        uintptr_t timeCount;
        char const *key;
        while ((timeCount = (uintptr_t) (iter->next(key))) != 0) {
#ifdef DEBUG
            fprintf(stderr, "reap: checking SSRC 0x%lx: %ld (threshold %d)\n", (unsigned long)key, timeCount, threshold);
#endif
            if (timeCount < (uintptr_t) threshold) { // this SSRC is old
                uintptr_t ssrc = (uintptr_t) key;
                oldSSRC = (u_int32_t) ssrc;
                foundOldMember = True;
            }
        }
        delete iter;

        if (foundOldMember) {
#ifdef DEBUG
            fprintf(stderr, "reap: removing SSRC 0x%x\n", oldSSRC);
#endif
            fOurRTCPInstance.removeSSRC(oldSSRC, True);
        }
    } while (foundOldMember);
}


////////// RTCPInstance //////////

static double dTimeNow() {
    struct timeval timeNow;
    gettimeofday(&timeNow, NULL);
    return (double) (timeNow.tv_sec + timeNow.tv_usec / 1000000.0);
}

static unsigned const maxRTCPPacketSize = 1456;
// bytes (1500, minus some allowance for IP, UDP, UMTP headers)
static unsigned const preferredRTCPPacketSize = 1000; // bytes

RTCPInstance::RTCPInstance(UsageEnvironment &env, Groupsock *RTCPgs,
                           unsigned totSessionBW,
                           unsigned char const *cname,
                           RTPSink *sink, RTPSource *source,
                           Boolean isSSMSource)
        : Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW),
          fSink(sink), fSource(source), fIsSSMSource(isSSMSource),
          fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1),
          fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0),
          fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0),
          fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE),
          fHaveJustSentPacket(False), fLastPacketSentSize(0),
          fByeHandlerTask(NULL), fByeHandlerClientData(NULL),
          fSRHandlerTask(NULL), fSRHandlerClientData(NULL),
          fRRHandlerTask(NULL), fRRHandlerClientData(NULL),
          fSpecificRRHandlerTable(NULL),
          fAppHandlerTask(NULL), fAppHandlerClientData(NULL) {
#ifdef DEBUG
    fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this);
#endif
    if (fTotSessionBW == 0) { // not allowed!
        env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n";
        fTotSessionBW = 1;
    }

    if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicast

    double timeNow = dTimeNow();
    fPrevReportTime = fNextReportTime = timeNow;

    fKnownMembers = new RTCPMemberDatabase(*this);
    fInBuf = new unsigned char[maxRTCPPacketSize];
    if (fKnownMembers == NULL || fInBuf == NULL) return;
    fNumBytesAlreadyRead = 0;

    fOutBuf = new OutPacketBuffer(preferredRTCPPacketSize, maxRTCPPacketSize, maxRTCPPacketSize);
    if (fOutBuf == NULL) return;

    if (fSource != NULL && fSource->RTPgs() == RTCPgs) {
        // We're receiving RTCP reports that are multiplexed with RTP, so ask the RTP source
        // to give them to us:
        fSource->registerForMultiplexedRTCPPackets(this);
    } else {
        // Arrange to handle incoming reports from the network:
        TaskScheduler::BackgroundHandlerProc *handler
                = (TaskScheduler::BackgroundHandlerProc *) &incomingReportHandler;
        fRTCPInterface.startNetworkReading(handler);
    }

    // Send our first report.
    fTypeOfEvent = EVENT_REPORT;
    onExpire(this);
}

struct RRHandlerRecord {
    TaskFunc *rrHandlerTask;
    void *rrHandlerClientData;
};

RTCPInstance::~RTCPInstance() {
#ifdef DEBUG
    fprintf(stderr, "RTCPInstance[%p]::~RTCPInstance()\n", this);
#endif
    // Begin by sending a BYE.  We have to do this immediately, without
    // 'reconsideration', because "this" is going away.
    fTypeOfEvent = EVENT_BYE; // not used, but...
    sendBYE();

    if (fSource != NULL && fSource->RTPgs() == fRTCPInterface.gs()) {
        // We were receiving RTCP reports that were multiplexed with RTP, so tell the RTP source
        // to stop giving them to us:
        fSource->deregisterForMultiplexedRTCPPackets();
        fRTCPInterface.forgetOurGroupsock();
        // so that the "fRTCPInterface" destructor doesn't turn off background read handling
    }

    if (fSpecificRRHandlerTable != NULL) {
        AddressPortLookupTable::Iterator iter(*fSpecificRRHandlerTable);
        RRHandlerRecord *rrHandler;
        while ((rrHandler = (RRHandlerRecord *) iter.next()) != NULL) {
            delete rrHandler;
        }
        delete fSpecificRRHandlerTable;
    }

    delete fKnownMembers;
    delete fOutBuf;
    delete[] fInBuf;
}

void RTCPInstance::noteArrivingRR(struct sockaddr_in const &fromAddressAndPort,
                                  int tcpSocketNum, unsigned char tcpStreamChannelId) {
    // If a 'RR handler' was set, call it now:

    // Specific RR handler:
    if (fSpecificRRHandlerTable != NULL) {
        netAddressBits fromAddr;
        portNumBits fromPortNum;
        if (tcpSocketNum < 0) {
            // Normal case: We read the RTCP packet over UDP
            fromAddr = fromAddressAndPort.sin_addr.s_addr;
            fromPortNum = ntohs(fromAddressAndPort.sin_port);
        } else {
            // Special case: We read the RTCP packet over TCP (interleaved)
            // Hack: Use the TCP socket and channel id to look up the handler
            fromAddr = tcpSocketNum;
            fromPortNum = tcpStreamChannelId;
        }
        Port fromPort(fromPortNum);
        RRHandlerRecord *rrHandler
                = (RRHandlerRecord *) (fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort));
        if (rrHandler != NULL) {
            if (rrHandler->rrHandlerTask != NULL) {
                (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData);
            }
        }
    }

    // General RR handler:
    if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData);
}

RTCPInstance *RTCPInstance::createNew(UsageEnvironment &env, Groupsock *RTCPgs,
                                      unsigned totSessionBW,
                                      unsigned char const *cname,
                                      RTPSink *sink, RTPSource *source,
                                      Boolean isSSMSource) {
    return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source,
                            isSSMSource);
}

Boolean RTCPInstance::lookupByName(UsageEnvironment &env,
                                   char const *instanceName,
                                   RTCPInstance *&resultInstance) {
    resultInstance = NULL; // unless we succeed

    Medium *medium;
    if (!Medium::lookupByName(env, instanceName, medium)) return False;

    if (!medium->isRTCPInstance()) {
        env.setResultMsg(instanceName, " is not a RTCP instance");
        return False;
    }

    resultInstance = (RTCPInstance *) medium;
    return True;
}

Boolean RTCPInstance::isRTCPInstance() const {
    return True;
}

unsigned RTCPInstance::numMembers() const {
    if (fKnownMembers == NULL) return 0;

    return fKnownMembers->numMembers();
}

void RTCPInstance::setByeHandler(TaskFunc *handlerTask, void *clientData,
                                 Boolean handleActiveParticipantsOnly) {
    fByeHandlerTask = handlerTask;
    fByeHandlerClientData = clientData;
    fByeHandleActiveParticipantsOnly = handleActiveParticipantsOnly;
}

void RTCPInstance::setSRHandler(TaskFunc *handlerTask, void *clientData) {
    fSRHandlerTask = handlerTask;
    fSRHandlerClientData = clientData;
}

void RTCPInstance::setRRHandler(TaskFunc *handlerTask, void *clientData) {
    fRRHandlerTask = handlerTask;
    fRRHandlerClientData = clientData;
}

void RTCPInstance
::setSpecificRRHandler(netAddressBits fromAddress, Port fromPort,
                       TaskFunc *handlerTask, void *clientData) {
    if (handlerTask == NULL && clientData == NULL) {
        unsetSpecificRRHandler(fromAddress, fromPort);
        return;
    }

    RRHandlerRecord *rrHandler = new RRHandlerRecord;
    rrHandler->rrHandlerTask = handlerTask;
    rrHandler->rrHandlerClientData = clientData;
    if (fSpecificRRHandlerTable == NULL) {
        fSpecificRRHandlerTable = new AddressPortLookupTable;
    }
    RRHandlerRecord *existingRecord = (RRHandlerRecord *) fSpecificRRHandlerTable->Add(fromAddress,
                                                                                       (~0),
                                                                                       fromPort,
                                                                                       rrHandler);
    delete existingRecord; // if any

}

void RTCPInstance
::unsetSpecificRRHandler(netAddressBits fromAddress, Port fromPort) {
    if (fSpecificRRHandlerTable == NULL) return;

    RRHandlerRecord *rrHandler
            = (RRHandlerRecord *) (fSpecificRRHandlerTable->Lookup(fromAddress, (~0), fromPort));
    if (rrHandler != NULL) {
        fSpecificRRHandlerTable->Remove(fromAddress, (~0), fromPort);
        delete rrHandler;
    }
}

void RTCPInstance::setAppHandler(RTCPAppHandlerFunc *handlerTask, void *clientData) {
    fAppHandlerTask = handlerTask;
    fAppHandlerClientData = clientData;
}

void RTCPInstance::sendAppPacket(u_int8_t subtype, char const *name,
                                 u_int8_t *appDependentData, unsigned appDependentDataSize) {
    // Set up the first 4 bytes: V,PT,subtype,PT,length:
    u_int32_t rtcpHdr = 0x80000000; // version 2, no padding
    rtcpHdr |= (subtype & 0x1F) << 24;
    rtcpHdr |= (RTCP_PT_APP << 16);
    unsigned length = 2 + (appDependentDataSize + 3) / 4;
    rtcpHdr |= (length & 0xFFFF);
    fOutBuf->enqueueWord(rtcpHdr);

    // Set up the next 4 bytes: SSRC:
    fOutBuf->enqueueWord(fSource != NULL ? fSource->SSRC() : fSink != NULL ? fSink->SSRC() : 0);

    // Set up the next 4 bytes: name:
    char nameBytes[4];
    nameBytes[0] = nameBytes[1] = nameBytes[2] = nameBytes[3] = '\0'; // by default
    if (name != NULL) {
        snprintf(nameBytes, 4, "%s", name);
    }
    fOutBuf->enqueue((u_int8_t *) nameBytes, 4);

    // Set up the remaining bytes (if any): application-dependent data (+ padding):
    if (appDependentData != NULL && appDependentDataSize > 0) {
        fOutBuf->enqueue(appDependentData, appDependentDataSize);

        unsigned modulo = appDependentDataSize % 4;
        unsigned paddingSize = modulo == 0 ? 0 : 4 - modulo;
        u_int8_t const paddingByte = 0x00;
        for (unsigned i = 0; i < paddingSize; ++i) fOutBuf->enqueue(&paddingByte, 1);
    }

    // Finally, send the packet:
    sendBuiltPacket();
}

void RTCPInstance::setStreamSocket(int sockNum,
                                   unsigned char streamChannelId) {
    // Turn off background read handling:
    fRTCPInterface.stopNetworkReading();

    // Switch to RTCP-over-TCP:
    fRTCPInterface.setStreamSocket(sockNum, streamChannelId);

    // Turn background reading back on:
    TaskScheduler::BackgroundHandlerProc *handler
            = (TaskScheduler::BackgroundHandlerProc *) &incomingReportHandler;
    fRTCPInterface.startNetworkReading(handler);
}

void RTCPInstance::addStreamSocket(int sockNum,
                                   unsigned char streamChannelId) {
    // First, turn off background read handling for the default (UDP) socket:
    envir().taskScheduler().turnOffBackgroundReadHandling(fRTCPInterface.gs()->socketNum());

    // Add the RTCP-over-TCP interface:
    fRTCPInterface.addStreamSocket(sockNum, streamChannelId);

    // Turn on background reading for this socket (in case it's not on already):
    TaskScheduler::BackgroundHandlerProc *handler
            = (TaskScheduler::BackgroundHandlerProc *) &incomingReportHandler;
    fRTCPInterface.startNetworkReading(handler);
}

void RTCPInstance
::injectReport(u_int8_t const *packet, unsigned packetSize, struct sockaddr_in const &fromAddress) {
    if (packetSize > maxRTCPPacketSize) packetSize = maxRTCPPacketSize;
    memmove(fInBuf, packet, packetSize);

    processIncomingReport(packetSize, fromAddress, -1, 0xFF); // assume report received over UDP
}

static unsigned const IP_UDP_HDR_SIZE = 28;
// overhead (bytes) of IP and UDP hdrs

#define ADVANCE(n) pkt += (n); packetSize -= (n)

void RTCPInstance::incomingReportHandler(RTCPInstance *instance,
                                         int /*mask*/) {
    instance->incomingReportHandler1();
}

void RTCPInstance::incomingReportHandler1() {
    do {
        if (fNumBytesAlreadyRead >= maxRTCPPacketSize) {
            envir()
                    << "RTCPInstance error: Hit limit when reading incoming packet over TCP. (fNumBytesAlreadyRead ("
                    << fNumBytesAlreadyRead << ") >= maxRTCPPacketSize (" << maxRTCPPacketSize
                    << ")).  The remote endpoint is using a buggy implementation of RTP/RTCP-over-TCP.  Please upgrade it!\n";
            break;
        }

        unsigned numBytesRead;
        struct sockaddr_in fromAddress;
        int tcpSocketNum;
        unsigned char tcpStreamChannelId;
        Boolean packetReadWasIncomplete;
        Boolean readResult
                = fRTCPInterface.handleRead(&fInBuf[fNumBytesAlreadyRead],
                                            maxRTCPPacketSize - fNumBytesAlreadyRead,
                                            numBytesRead, fromAddress,
                                            tcpSocketNum, tcpStreamChannelId,
                                            packetReadWasIncomplete);

        unsigned packetSize = 0;
        if (packetReadWasIncomplete) {
            fNumBytesAlreadyRead += numBytesRead;
            return; // more reads are needed to get the entire packet
        } else { // normal case: We've read the entire packet
            packetSize = fNumBytesAlreadyRead + numBytesRead;
            fNumBytesAlreadyRead = 0; // for next time
        }
        if (!readResult) break;

        // Ignore the packet if it was looped-back from ourself:
        Boolean packetWasFromOurHost = False;
        if (RTCPgs()->wasLoopedBackFromUs(envir(), fromAddress)) {
            packetWasFromOurHost = True;
            // However, we still want to handle incoming RTCP packets from
            // *other processes* on the same machine.  To distinguish this
            // case from a true loop-back, check whether we've just sent a
            // packet of the same size.  (This check isn't perfect, but it seems
            // to be the best we can do.)
            if (fHaveJustSentPacket && fLastPacketSentSize == packetSize) {
                // This is a true loop-back:
                fHaveJustSentPacket = False;
                break; // ignore this packet
            }
        }

        if (fIsSSMSource && !packetWasFromOurHost) {
            // This packet is assumed to have been received via unicast (because we're a SSM source,
            // and SSM receivers send back RTCP "RR" packets via unicast).
            // 'Reflect' the packet by resending it to the multicast group, so that any other receivers
            // can also get to see it.

            // NOTE: Denial-of-service attacks are possible here.
            // Users of this software may wish to add their own,
            // application-specific mechanism for 'authenticating' the
            // validity of this packet before reflecting it.

            // NOTE: The test for "!packetWasFromOurHost" means that we won't reflect RTCP packets
            // that come from other processes on the same host as us.  The reason for this is that the
            // 'packet size' test above is not 100% reliable; some packets that were truly looped back
            // from us might not be detected as such, and this might lead to infinite
            // forwarding/receiving of some packets.  To avoid this possibility, we reflect only
            // RTCP packets that we know for sure originated elsewhere.
            // (Note, though, that if we ever re-enable the code in "Groupsock::multicastSendOnly()",
            // then we could remove the test for "!packetWasFromOurHost".)
            fRTCPInterface.sendPacket(fInBuf, packetSize);
            fHaveJustSentPacket = True;
            fLastPacketSentSize = packetSize;
        }

        processIncomingReport(packetSize, fromAddress, tcpSocketNum, tcpStreamChannelId);
    } while (0);
}

void RTCPInstance
::processIncomingReport(unsigned packetSize, struct sockaddr_in const &fromAddressAndPort,
                        int tcpSocketNum, unsigned char tcpStreamChannelId) {
    do {
        Boolean callByeHandler = False;
        unsigned char *pkt = fInBuf;

#ifdef DEBUG
        fprintf(stderr, "[%p]saw incoming RTCP packet (from ", this);
        if (tcpSocketNum < 0) {
          // Note that "fromAddressAndPort" is valid only if we're receiving over UDP (not over TCP):
          fprintf(stderr, "address %s, port %d", AddressString(fromAddressAndPort).val(), ntohs(fromAddressAndPort.sin_port));
        } else {
          fprintf(stderr, "TCP socket #%d, stream channel id %d", tcpSocketNum, tcpStreamChannelId);
        }
        fprintf(stderr, ")\n");
        for (unsigned i = 0; i < packetSize; ++i) {
          if (i%4 == 0) fprintf(stderr, " ");
          fprintf(stderr, "%02x", pkt[i]);
        }
        fprintf(stderr, "\n");
#endif
        int totPacketSize = IP_UDP_HDR_SIZE + packetSize;

        // Check the RTCP packet for validity:
        // It must at least contain a header (4 bytes), and this header
        // must be version=2, with no padding bit, and a payload type of
        // SR (200), RR (201), or APP (204):
        if (packetSize < 4) break;
        unsigned rtcpHdr = ntohl(*(u_int32_t *) pkt);
        if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR << 16)) &&
            (rtcpHdr & 0xE0FF0000) != (0x80000000 | (RTCP_PT_APP << 16))) {
#ifdef DEBUG
            fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n", rtcpHdr);
#endif
            break;
        }

        // Process each of the individual RTCP 'subpackets' in (what may be)
        // a compound RTCP packet.
        int typeOfPacket = PACKET_UNKNOWN_TYPE;
        unsigned reportSenderSSRC = 0;
        Boolean packetOK = False;
        while (1) {
            u_int8_t rc = (rtcpHdr >> 24) & 0x1F;
            u_int8_t pt = (rtcpHdr >> 16) & 0xFF;
            unsigned length = 4 * (rtcpHdr & 0xFFFF); // doesn't count hdr
            ADVANCE(4); // skip over the header
            if (length > packetSize) break;

            // Assume that each RTCP subpacket begins with a 4-byte SSRC:
            if (length < 4) break;
            length -= 4;
            reportSenderSSRC = ntohl(*(u_int32_t *) pkt);
            ADVANCE(4);
#ifdef HACK_FOR_CHROME_WEBRTC_BUG
            if (reportSenderSSRC == 0x00000001 && pt == RTCP_PT_RR) {
                // Chrome (and Opera) WebRTC receivers have a bug that causes them to always send
                // SSRC 1 in their "RR"s.  To work around this (to help us distinguish between different
                // receivers), we use a fake SSRC in this case consisting of the IP address, XORed with
                // the port number:
                reportSenderSSRC = fromAddressAndPort.sin_addr.s_addr ^ fromAddressAndPort.sin_port;
            }
#endif

            Boolean subPacketOK = False;
            switch (pt) {
                case RTCP_PT_SR: {
#ifdef DEBUG
                    fprintf(stderr, "SR\n");
#endif
                    if (length < 20) break;
                    length -= 20;

                    // Extract the NTP timestamp, and note this:
                    unsigned NTPmsw = ntohl(*(u_int32_t *) pkt);
                    ADVANCE(4);
                    unsigned NTPlsw = ntohl(*(u_int32_t *) pkt);
                    ADVANCE(4);
                    unsigned rtpTimestamp = ntohl(*(u_int32_t *) pkt);
                    ADVANCE(4);
                    if (fSource != NULL) {
                        RTPReceptionStatsDB &receptionStats
                                = fSource->receptionStatsDB();
                        receptionStats.noteIncomingSR(reportSenderSSRC,
                                                      NTPmsw, NTPlsw, rtpTimestamp);
                    }
                    ADVANCE(8); // skip over packet count, octet count

                    // If a 'SR handler' was set, call it now:
                    if (fSRHandlerTask != NULL) (*fSRHandlerTask)(fSRHandlerClientData);

                    // The rest of the SR is handled like a RR (so, no "break;" here)
                }
                case RTCP_PT_RR: {
#ifdef DEBUG
                    fprintf(stderr, "RR\n");
#endif
                    unsigned reportBlocksSize = rc * (6 * 4);
                    if (length < reportBlocksSize) break;
                    length -= reportBlocksSize;

                    if (fSink != NULL) {
                        // Use this information to update stats about our transmissions:
                        RTPTransmissionStatsDB &transmissionStats = fSink->transmissionStatsDB();
                        for (unsigned i = 0; i < rc; ++i) {
                            unsigned senderSSRC = ntohl(*(u_int32_t *) pkt);
                            ADVANCE(4);
                            // We care only about reports about our own transmission, not others'
                            if (senderSSRC == fSink->SSRC()) {
                                unsigned lossStats = ntohl(*(u_int32_t *) pkt);
                                ADVANCE(4);
                                unsigned highestReceived = ntohl(*(u_int32_t *) pkt);
                                ADVANCE(4);
                                unsigned jitter = ntohl(*(u_int32_t *) pkt);
                                ADVANCE(4);
                                unsigned timeLastSR = ntohl(*(u_int32_t *) pkt);
                                ADVANCE(4);
                                unsigned timeSinceLastSR = ntohl(*(u_int32_t *) pkt);
                                ADVANCE(4);
                                transmissionStats.noteIncomingRR(reportSenderSSRC,
                                                                 fromAddressAndPort,
                                                                 lossStats,
                                                                 highestReceived, jitter,
                                                                 timeLastSR, timeSinceLastSR);
                            } else {
                                ADVANCE(4 * 5);
                            }
                        }
                    } else {
                        ADVANCE(reportBlocksSize);
                    }

                    if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR'
                        noteArrivingRR(fromAddressAndPort, tcpSocketNum, tcpStreamChannelId);
                    }

                    subPacketOK = True;
                    typeOfPacket = PACKET_RTCP_REPORT;
                    break;
                }
                case RTCP_PT_BYE: {
#ifdef DEBUG
                    fprintf(stderr, "BYE\n");
#endif
                    // If a 'BYE handler' was set, arrange for it to be called at the end of this routine.
                    // (Note: We don't call it immediately, in case it happens to cause "this" to be deleted.)
                    if (fByeHandlerTask != NULL
                        && (!fByeHandleActiveParticipantsOnly
                            || (fSource != NULL
                                && fSource->receptionStatsDB().lookup(reportSenderSSRC) != NULL)
                            || (fSink != NULL
                                &&
                                fSink->transmissionStatsDB().lookup(reportSenderSSRC) != NULL))) {
                        callByeHandler = True;
                    }

                    // We should really check for & handle >1 SSRCs being present #####

                    subPacketOK = True;
                    typeOfPacket = PACKET_BYE;
                    break;
                }
                case RTCP_PT_APP: {
                    u_int8_t &subtype = rc; // In "APP" packets, the "rc" field gets used as "subtype"
#ifdef DEBUG
                    fprintf(stderr, "APP (subtype 0x%02x)\n", subtype);
#endif
                    if (length < 4) {
#ifdef DEBUG
                        fprintf(stderr, "\tError: No \"name\" field!\n");
#endif
                        break;
                    }
                    length -= 4;
#ifdef DEBUG
                    fprintf(stderr, "\tname:%c%c%c%c\n", pkt[0], pkt[1], pkt[2], pkt[3]);
#endif
                    u_int32_t nameBytes =
                            (pkt[0] << 24) | (pkt[1] << 16) | (pkt[2] << 8) | (pkt[3]);
                    ADVANCE(4); // skip over "name", to the 'application-dependent data'
#ifdef DEBUG
                    fprintf(stderr, "\tapplication-dependent data size: %d bytes\n", length);
#endif

                    // If an 'APP' packet handler was set, call it now:
                    if (fAppHandlerTask != NULL) {
                        (*fAppHandlerTask)(fAppHandlerClientData, subtype, nameBytes, pkt, length);
                    }
                    subPacketOK = True;
                    typeOfPacket = PACKET_RTCP_APP;
                    break;
                }
                    // Other RTCP packet types that we don't yet handle:
                case RTCP_PT_SDES: {
#ifdef DEBUG
                    // 'Handle' SDES packets only in debugging code, by printing out the 'SDES items':
                    fprintf(stderr, "SDES\n");

                    // Process each 'chunk':
                    Boolean chunkOK = False;
                    ADVANCE(-4); length += 4; // hack so that we see the first SSRC/CSRC again
                    while (length >= 8) { // A valid chunk must be at least 8 bytes long
                      chunkOK = False; // until we learn otherwise

                      u_int32_t SSRC_CSRC = ntohl(*(u_int32_t*)pkt); ADVANCE(4); length -= 4;
                      fprintf(stderr, "\tSSRC/CSRC: 0x%08x\n", SSRC_CSRC);

                      // Process each 'SDES item' in the chunk:
                      u_int8_t itemType = *pkt; ADVANCE(1); --length;
                      while (itemType != 0) {
                        unsigned itemLen = *pkt; ADVANCE(1); --length;
                        // Make sure "itemLen" allows for at least 1 zero byte at the end of the chunk:
                        if (itemLen + 1 > length || pkt[itemLen] != 0) break;

                        fprintf(stderr, "\t\t%s:%s\n",
                            itemType == 1 ? "CNAME" :
                            itemType == 2 ? "NAME" :
                            itemType == 3 ? "EMAIL" :
                            itemType == 4 ? "PHONE" :
                            itemType == 5 ? "LOC" :
                            itemType == 6 ? "TOOL" :
                            itemType == 7 ? "NOTE" :
                            itemType == 8 ? "PRIV" :
                            "(unknown)",
                            itemType < 8 ? (char*)pkt // hack, because we know it's '\0'-terminated
                            : "???"/* don't try to print out PRIV or unknown items */);
                        ADVANCE(itemLen); length -= itemLen;

                        itemType = *pkt; ADVANCE(1); --length;
                      }
                      if (itemType != 0) break; // bad 'SDES item'

                      // Thus, itemType == 0.  This zero 'type' marks the end of the list of SDES items.
                      // Skip over remaining zero padding bytes, so that this chunk ends on a 4-byte boundary:
                      while (length%4 > 0 && *pkt == 0) { ADVANCE(1); --length; }
                      if (length%4 > 0) break; // Bad (non-zero) padding byte

                      chunkOK = True;
                    }
                    if (!chunkOK || length > 0) break; // bad chunk, or not enough bytes for the last chunk
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_RTPFB: {
#ifdef DEBUG
                    fprintf(stderr, "RTPFB(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_PSFB: {
#ifdef DEBUG
                    fprintf(stderr, "PSFB(unhandled)\n");
                    // Temporary code to show "Receiver Estimated Maximum Bitrate" (REMB) feedback reports:
                    //#####
                    if (length >= 12 && pkt[4] == 'R' && pkt[5] == 'E' && pkt[6] == 'M' && pkt[7] == 'B') {
                      u_int8_t exp = pkt[9]>>2;
                      u_int32_t mantissa = ((pkt[9]&0x03)<<16)|(pkt[10]<<8)|pkt[11];
                      double remb = (double)mantissa;
                      while (exp > 0) {
                        remb *= 2.0;
                        exp /= 2;
                      }
                      fprintf(stderr, "\tReceiver Estimated Max Bitrate (REMB): %g bps\n", remb);
                    }
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_XR: {
#ifdef DEBUG
                    fprintf(stderr, "XR(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_AVB: {
#ifdef DEBUG
                    fprintf(stderr, "AVB(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_RSI: {
#ifdef DEBUG
                    fprintf(stderr, "RSI(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_TOKEN: {
#ifdef DEBUG
                    fprintf(stderr, "TOKEN(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                case RTCP_PT_IDMS: {
#ifdef DEBUG
                    fprintf(stderr, "IDMS(unhandled)\n");
#endif
                    subPacketOK = True;
                    break;
                }
                default: {
#ifdef DEBUG
                    fprintf(stderr, "UNKNOWN TYPE(0x%x)\n", pt);
#endif
                    subPacketOK = True;
                    break;
                }
            }
            if (!subPacketOK) break;

                // need to check for (& handle) SSRC collision! #####

#ifdef DEBUG
                fprintf(stderr, "validated RTCP subpacket: rc:%d, pt:%d, bytes remaining:%d, report sender SSRC:0x%08x\n", rc, pt, length, reportSenderSSRC);
#endif

            // Skip over any remaining bytes in this subpacket:
            ADVANCE(length);

            // Check whether another RTCP 'subpacket' follows:
            if (packetSize == 0) {
                packetOK = True;
                break;
            } else if (packetSize < 4) {
#ifdef DEBUG
                fprintf(stderr, "extraneous %d bytes at end of RTCP packet!\n", packetSize);
#endif
                break;
            }
            rtcpHdr = ntohl(*(u_int32_t *) pkt);
            if ((rtcpHdr & 0xC0000000) != 0x80000000) {
#ifdef DEBUG
                fprintf(stderr, "bad RTCP subpacket: header 0x%08x\n", rtcpHdr);
#endif
                break;
            }
        }

        if (!packetOK) {
#ifdef DEBUG
            fprintf(stderr, "rejected bad RTCP subpacket: header 0x%08x\n", rtcpHdr);
#endif
            break;
        } else {
#ifdef DEBUG
            fprintf(stderr, "validated entire RTCP packet\n");
#endif
        }

        onReceive(typeOfPacket, totPacketSize, reportSenderSSRC);

        // Finally, if we need to call a "BYE" handler, do so now (in case it causes "this" to get deleted):
        if (callByeHandler && fByeHandlerTask != NULL/*sanity check*/) {
            TaskFunc *byeHandler = fByeHandlerTask;
            fByeHandlerTask = NULL; // because we call the handler only once, by default
            (*byeHandler)(fByeHandlerClientData);
        }
    } while (0);
}

void RTCPInstance::onReceive(int typeOfPacket, int totPacketSize, u_int32_t ssrc) {
    fTypeOfPacket = typeOfPacket;
    fLastReceivedSize = totPacketSize;
    fLastReceivedSSRC = ssrc;

    int members = (int) numMembers();
    int senders = (fSink != NULL) ? 1 : 0;

    OnReceive(this, // p
              this, // e
              &members, // members
              &fPrevNumMembers, // pmembers
              &senders, // senders
              &fAveRTCPSize, // avg_rtcp_size
              &fPrevReportTime, // tp
              dTimeNow(), // tc
              fNextReportTime);
}

void RTCPInstance::sendReport() {
#ifdef DEBUG
    fprintf(stderr, "sending REPORT\n");
#endif
    // Begin by including a SR and/or RR report:
    if (!addReport()) return;

    // Then, include a SDES:
    addSDES();

    // Send the report:
    sendBuiltPacket();

    // Periodically clean out old members from our SSRC membership database:
    const unsigned membershipReapPeriod = 5;
    if ((++fOutgoingReportCount) % membershipReapPeriod == 0) {
        unsigned threshold = fOutgoingReportCount - membershipReapPeriod;
        fKnownMembers->reapOldMembers(threshold);
    }
}

void RTCPInstance::sendBYE() {
#ifdef DEBUG
    fprintf(stderr, "sending BYE\n");
#endif
    // The packet must begin with a SR and/or RR report:
    (void) addReport(True);

    addBYE();
    sendBuiltPacket();
}

void RTCPInstance::sendBuiltPacket() {
#ifdef DEBUG
    fprintf(stderr, "sending RTCP packet\n");
    unsigned char* p = fOutBuf->packet();
    for (unsigned i = 0; i < fOutBuf->curPacketSize(); ++i) {
      if (i%4 == 0) fprintf(stderr," ");
      fprintf(stderr, "%02x", p[i]);
    }
    fprintf(stderr, "\n");
#endif
    unsigned reportSize = fOutBuf->curPacketSize();
    fRTCPInterface.sendPacket(fOutBuf->packet(), reportSize);
    fOutBuf->resetOffset();

    fLastSentSize = IP_UDP_HDR_SIZE + reportSize;
    fHaveJustSentPacket = True;
    fLastPacketSentSize = reportSize;
}

int RTCPInstance::checkNewSSRC() {
    return fKnownMembers->noteMembership(fLastReceivedSSRC,
                                         fOutgoingReportCount);
}

void RTCPInstance::removeLastReceivedSSRC() {
    removeSSRC(fLastReceivedSSRC, False/*keep stats around*/);
}

void RTCPInstance::removeSSRC(u_int32_t ssrc, Boolean alsoRemoveStats) {
    fKnownMembers->remove(ssrc);

    if (alsoRemoveStats) {
        // Also, remove records of this SSRC from any reception or transmission stats
        if (fSource != NULL) fSource->receptionStatsDB().removeRecord(ssrc);
        if (fSink != NULL) fSink->transmissionStatsDB().removeRecord(ssrc);
    }
}

void RTCPInstance::onExpire(RTCPInstance *instance) {
    instance->onExpire1();
}

// Member functions to build specific kinds of report:

Boolean RTCPInstance::addReport(Boolean alwaysAdd) {
    // Include a SR or a RR, depending on whether we have an associated sink or source:
    if (fSink != NULL) {
        if (!alwaysAdd) {
            if (!fSink->enableRTCPReports()) return False;

            // Hack: Don't send a SR during those (brief) times when the timestamp of the
            // next outgoing RTP packet has been preset, to ensure that that timestamp gets
            // used for that outgoing packet. (David Bertrand, 2006.07.18)
            if (fSink->nextTimestampHasBeenPreset()) return False;
        }

        addSR();
    }
    if (fSource != NULL) {
        if (!alwaysAdd) {
            if (!fSource->enableRTCPReports()) return False;
        }

        addRR();
    }

    return True;
}

void RTCPInstance::addSR() {
    // ASSERT: fSink != NULL

    enqueueCommonReportPrefix(RTCP_PT_SR, fSink->SSRC(),
                              5 /* extra words in a SR */);

    // Now, add the 'sender info' for our sink

    // Insert the NTP and RTP timestamps for the 'wallclock time':
    struct timeval timeNow;
    gettimeofday(&timeNow, NULL);
    fOutBuf->enqueueWord(timeNow.tv_sec + 0x83AA7E80);
    // NTP timestamp most-significant word (1970 epoch -> 1900 epoch)
    double fractionalPart = (timeNow.tv_usec / 15625.0) * 0x04000000; // 2^32/10^6
    fOutBuf->enqueueWord((unsigned) (fractionalPart + 0.5));
    // NTP timestamp least-significant word
    unsigned rtpTimestamp = fSink->convertToRTPTimestamp(timeNow);
    fOutBuf->enqueueWord(rtpTimestamp); // RTP ts

    // Insert the packet and byte counts:
    fOutBuf->enqueueWord(fSink->packetCount());
    fOutBuf->enqueueWord(fSink->octetCount());

    enqueueCommonReportSuffix();
}

void RTCPInstance::addRR() {
    // ASSERT: fSource != NULL

    enqueueCommonReportPrefix(RTCP_PT_RR, fSource->SSRC());
    enqueueCommonReportSuffix();
}

void RTCPInstance::enqueueCommonReportPrefix(unsigned char packetType,
                                             u_int32_t SSRC,
                                             unsigned numExtraWords) {
    unsigned numReportingSources;
    if (fSource == NULL) {
        numReportingSources = 0; // we don't receive anything
    } else {
        RTPReceptionStatsDB &allReceptionStats
                = fSource->receptionStatsDB();
        numReportingSources = allReceptionStats.numActiveSourcesSinceLastReset();
        // This must be <32, to fit in 5 bits:
        if (numReportingSources >= 32) { numReportingSources = 32; }
        // Later: support adding more reports to handle >32 sources (unlikely)#####
    }

    unsigned rtcpHdr = 0x80000000; // version 2, no padding
    rtcpHdr |= (numReportingSources << 24);
    rtcpHdr |= (packetType << 16);
    rtcpHdr |= (1 + numExtraWords + 6 * numReportingSources);
    // each report block is 6 32-bit words long
    fOutBuf->enqueueWord(rtcpHdr);

    fOutBuf->enqueueWord(SSRC);
}

void RTCPInstance::enqueueCommonReportSuffix() {
    // Output the report blocks for each source:
    if (fSource != NULL) {
        RTPReceptionStatsDB &allReceptionStats
                = fSource->receptionStatsDB();

        RTPReceptionStatsDB::Iterator iterator(allReceptionStats);
        while (1) {
            RTPReceptionStats *receptionStats = iterator.next();
            if (receptionStats == NULL) break;
            enqueueReportBlock(receptionStats);
        }

        allReceptionStats.reset(); // because we have just generated a report
    }
}

void
RTCPInstance::enqueueReportBlock(RTPReceptionStats *stats) {
    fOutBuf->enqueueWord(stats->SSRC());

    unsigned highestExtSeqNumReceived = stats->highestExtSeqNumReceived();

    unsigned totNumExpected
            = highestExtSeqNumReceived - stats->baseExtSeqNumReceived();
    int totNumLost = totNumExpected - stats->totNumPacketsReceived();
    // 'Clamp' this loss number to a 24-bit signed value:
    if (totNumLost > 0x007FFFFF) {
        totNumLost = 0x007FFFFF;
    } else if (totNumLost < 0) {
        if (totNumLost < -0x00800000) totNumLost = 0x00800000; // unlikely, but...
        totNumLost &= 0x00FFFFFF;
    }

    unsigned numExpectedSinceLastReset
            = highestExtSeqNumReceived - stats->lastResetExtSeqNumReceived();
    int numLostSinceLastReset
            = numExpectedSinceLastReset - stats->numPacketsReceivedSinceLastReset();
    unsigned char lossFraction;
    if (numExpectedSinceLastReset == 0 || numLostSinceLastReset < 0) {
        lossFraction = 0;
    } else {
        lossFraction = (unsigned char)
                ((numLostSinceLastReset << 8) / numExpectedSinceLastReset);
    }

    fOutBuf->enqueueWord((lossFraction << 24) | totNumLost);
    fOutBuf->enqueueWord(highestExtSeqNumReceived);

    fOutBuf->enqueueWord(stats->jitter());

    unsigned NTPmsw = stats->lastReceivedSR_NTPmsw();
    unsigned NTPlsw = stats->lastReceivedSR_NTPlsw();
    unsigned LSR = ((NTPmsw & 0xFFFF) << 16) | (NTPlsw >> 16); // middle 32 bits
    fOutBuf->enqueueWord(LSR);

    // Figure out how long has elapsed since the last SR rcvd from this src:
    struct timeval const &LSRtime = stats->lastReceivedSR_time(); // "last SR"
    struct timeval timeNow, timeSinceLSR;
    gettimeofday(&timeNow, NULL);
    if (timeNow.tv_usec < LSRtime.tv_usec) {
        timeNow.tv_usec += 1000000;
        timeNow.tv_sec -= 1;
    }
    timeSinceLSR.tv_sec = timeNow.tv_sec - LSRtime.tv_sec;
    timeSinceLSR.tv_usec = timeNow.tv_usec - LSRtime.tv_usec;
    // The enqueued time is in units of 1/65536 seconds.
    // (Note that 65536/1000000 == 1024/15625)
    unsigned DLSR;
    if (LSR == 0) {
        DLSR = 0;
    } else {
        DLSR = (timeSinceLSR.tv_sec << 16)
               | ((((timeSinceLSR.tv_usec << 11) + 15625) / 31250) & 0xFFFF);
    }
    fOutBuf->enqueueWord(DLSR);
}

void RTCPInstance::addSDES() {
    // For now we support only the CNAME item; later support more #####

    // Begin by figuring out the size of the entire SDES report:
    unsigned numBytes = 4;
    // counts the SSRC, but not the header; it'll get subtracted out
    numBytes += fCNAME.totalSize(); // includes id and length
    numBytes += 1; // the special END item

    unsigned num4ByteWords = (numBytes + 3) / 4;

    unsigned rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC chunk
    rtcpHdr |= (RTCP_PT_SDES << 16);
    rtcpHdr |= num4ByteWords;
    fOutBuf->enqueueWord(rtcpHdr);

    if (fSource != NULL) {
        fOutBuf->enqueueWord(fSource->SSRC());
    } else if (fSink != NULL) {
        fOutBuf->enqueueWord(fSink->SSRC());
    }

    // Add the CNAME:
    fOutBuf->enqueue(fCNAME.data(), fCNAME.totalSize());

    // Add the 'END' item (i.e., a zero byte), plus any more needed to pad:
    unsigned numPaddingBytesNeeded = 4 - (fOutBuf->curPacketSize() % 4);
    unsigned char const zero = '\0';
    while (numPaddingBytesNeeded-- > 0) fOutBuf->enqueue(&zero, 1);
}

void RTCPInstance::addBYE() {
    unsigned rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC
    rtcpHdr |= (RTCP_PT_BYE << 16);
    rtcpHdr |= 1; // 2 32-bit words total (i.e., with 1 SSRC)
    fOutBuf->enqueueWord(rtcpHdr);

    if (fSource != NULL) {
        fOutBuf->enqueueWord(fSource->SSRC());
    } else if (fSink != NULL) {
        fOutBuf->enqueueWord(fSink->SSRC());
    }
}

void RTCPInstance::schedule(double nextTime) {
    fNextReportTime = nextTime;

    double secondsToDelay = nextTime - dTimeNow();
    if (secondsToDelay < 0) secondsToDelay = 0;
#ifdef DEBUG
    fprintf(stderr, "schedule(%f->%f)\n", secondsToDelay, nextTime);
#endif
    int64_t usToGo = (int64_t) (secondsToDelay * 1000000);
    nextTask() = envir().taskScheduler().scheduleDelayedTask(usToGo,
                                                             (TaskFunc *) RTCPInstance::onExpire,
                                                             this);
}

void RTCPInstance::reschedule(double nextTime) {
    envir().taskScheduler().unscheduleDelayedTask(nextTask());
    schedule(nextTime);
}

void RTCPInstance::onExpire1() {
    nextTask() = NULL;

    // Note: fTotSessionBW is kbits per second
    double rtcpBW = 0.05 * fTotSessionBW * 1024 / 8; // -> bytes per second

    OnExpire(this, // event
             numMembers(), // members
             (fSink != NULL) ? 1 : 0, // senders
             rtcpBW, // rtcp_bw
             (fSink != NULL) ? 1 : 0, // we_sent
             &fAveRTCPSize, // ave_rtcp_size
             &fIsInitial, // initial
             dTimeNow(), // tc
             &fPrevReportTime, // tp
             &fPrevNumMembers // pmembers
    );
}

////////// SDESItem //////////

SDESItem::SDESItem(unsigned char tag, unsigned char const *value) {
    unsigned length = strlen((char const *) value);
    if (length > 0xFF) length = 0xFF; // maximum data length for a SDES item

    fData[0] = tag;
    fData[1] = (unsigned char) length;
    memmove(&fData[2], value, length);
}

unsigned SDESItem::totalSize() const {
    return 2 + (unsigned) fData[1];
}


////////// Implementation of routines imported by the "rtcp_from_spec" C code

extern "C" void Schedule(double nextTime, event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return;

    instance->schedule(nextTime);
}

extern "C" void Reschedule(double nextTime, event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return;

    instance->reschedule(nextTime);
}

extern "C" void SendRTCPReport(event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return;

    instance->sendReport();
}

extern "C" void SendBYEPacket(event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return;

    instance->sendBYE();
}

extern "C" int TypeOfEvent(event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return EVENT_UNKNOWN;

    return instance->typeOfEvent();
}

extern "C" int SentPacketSize(event e) {
    RTCPInstance *instance = (RTCPInstance *) e;
    if (instance == NULL) return 0;

    return instance->sentPacketSize();
}

extern "C" int PacketType(packet p) {
    RTCPInstance *instance = (RTCPInstance *) p;
    if (instance == NULL) return PACKET_UNKNOWN_TYPE;

    return instance->packetType();
}

extern "C" int ReceivedPacketSize(packet p) {
    RTCPInstance *instance = (RTCPInstance *) p;
    if (instance == NULL) return 0;

    return instance->receivedPacketSize();
}

extern "C" int NewMember(packet p) {
    RTCPInstance *instance = (RTCPInstance *) p;
    if (instance == NULL) return 0;

    return instance->checkNewSSRC();
}

extern "C" int NewSender(packet /*p*/) {
    return 0; // we don't yet recognize senders other than ourselves #####
}

extern "C" void AddMember(packet /*p*/) {
    // Do nothing; all of the real work was done when NewMember() was called
}

extern "C" void AddSender(packet /*p*/) {
    // we don't yet recognize senders other than ourselves #####
}

extern "C" void RemoveMember(packet p) {
    RTCPInstance *instance = (RTCPInstance *) p;
    if (instance == NULL) return;

    instance->removeLastReceivedSSRC();
}

extern "C" void RemoveSender(packet /*p*/) {
    // we don't yet recognize senders other than ourselves #####
}

extern "C" double drand30() {
    unsigned tmp = our_random() & 0x3FFFFFFF; // a random 30-bit integer
    return tmp / (double) (1024 * 1024 * 1024);
}
